package com.zh.note.thread;

import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Locale;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 线程工厂
 */
public final class ThreadPoolFactory {


    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolFactory.class);
    private static final long KEEP_ALIVE_TIME = 60;

    private ThreadPoolFactory() {

    }

    /**
     * 创建一个单例线程池
     *
     * @param threadName
     * @return
     */
    public static ThreadPoolExecutor newSingleThreadPoolExecutor(String threadName) {
        return newThreadPoolExecutor(threadName, 1, 1, 2000);
    }

    /**
     * 创建线程池
     *
     * @param corePoolSize
     * @param maximumPoolSize
     * @param workQueueSize
     * @param handler
     * @return
     */
    public static ThreadPoolExecutor newThreadPoolExecutor(String threadName,
                                                           int corePoolSize,
                                                           int maximumPoolSize,
                                                           int workQueueSize,
                                                           RejectedExecutionHandler handler) {
        return new ThreadPoolExecutor(corePoolSize,
                maximumPoolSize,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(workQueueSize),
                new NamedThreadFactory("mi", threadName, false),
                handler);
    }

    /**
     * 创建一个线程池
     *
     * @param corePoolSize
     * @param maximumPoolSize
     * @param workQueueSize
     * @return
     */
    public static ThreadPoolExecutor newThreadPoolExecutor(String threadName, int corePoolSize, int maximumPoolSize, int workQueueSize) {
        return newThreadPoolExecutor(threadName, corePoolSize, maximumPoolSize, workQueueSize, new ThreadPoolExecutor.DiscardOldestPolicy());
    }

    /**
     * 自定义线程工厂
     */
    @Getter
    private static class NamedThreadFactory implements ThreadFactory {

        private static final AtomicInteger POOL_COUNTER = new AtomicInteger(0);

        private final Map<String, ThreadGroup> threadGroups = new ConcurrentHashMap<>();

        private final int poolId;

        private final Boolean daemon;
        private final ThreadGroup threadGroup;
        private final AtomicLong counter = new AtomicLong(0L);
        private final String prefix;

        /**
         * @param groupName 线程组名称
         * @param prefix    线程名称
         * @param daemon    是否设置守护线程
         */
        public NamedThreadFactory(String groupName, String prefix, Boolean daemon) {
            this.poolId = POOL_COUNTER.incrementAndGet();
            this.prefix = prefix;
            this.daemon = daemon;
            this.threadGroup = initThreadGroup(groupName);
        }

        private ThreadGroup initThreadGroup(String groupName) {
            threadGroups.putIfAbsent(groupName, new ThreadGroup(groupName));

            return threadGroups.get(groupName);
        }


        @Override
        public Thread newThread(Runnable runnable) {
            String name = String.format(Locale.ENGLISH, "Pool-%d-%s-%d", poolId, prefix, counter.incrementAndGet());
            Thread thread = new Thread(this.threadGroup, runnable, name);
            thread.setUncaughtExceptionHandler((currentThread, ex) -> {
                LOGGER.error("thread error: {}", currentThread.getName());
                LOGGER.error("thread error:", ex);
            });

            return thread;
        }
    }
}

